package org.pentaho.di.ui.trans.steps.shentong.oss.client;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.pentaho.di.ui.trans.steps.shentong.oss.client.Base64;
import org.pentaho.di.ui.trans.steps.shentong.oss.client.ObjectHandler;
import org.pentaho.di.ui.trans.steps.shentong.oss.client.ObjectResult;
import org.pentaho.di.ui.trans.steps.shentong.oss.client.OssError;
import org.pentaho.di.ui.trans.steps.shentong.oss.client.RequestBody;
import org.pentaho.di.ui.trans.steps.shentong.oss.client.ResponseBody;

public class OssClient {
	public static final String REQUEST_METHOD_GET = "GET";
	public static final String REQUEST_METHOD_POST = "POST";
	public static final String REQUEST_METHOD_PUT = "PUT";
	public static final String REQUEST_METHOD_DELETE = "DELETE";
	public static final String REQUEST_METHOD_HEAD = "HEAD";
	private String host;
	private String user;
	private String passwd;

	
	
	public OssClient(String host, String user, String passwd) {
		super();
		this.host = host;
		this.user = user;
		this.passwd = passwd;
	}

	public String getPasswd() {
		return passwd;
	}

	public void setPasswd(String passwd) {
		this.passwd = passwd;
	}

	public String getAuthorization() {
		try {
			return new String(Base64.encode((user + ":" + passwd).getBytes(StandardCharsets.UTF_8)),
					StandardCharsets.UTF_8);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

    @SuppressWarnings({ "rawtypes", "unchecked" })
	public ResponseBody execute(String sUrl, RequestBody requestBody) {
        if (requestBody == null) {
            throw new RuntimeException("RequestBody is null");
        } 
        sUrl = host + sUrl;
        HttpURLConnection conn = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        InputStream errorStream = null;

        ResponseBody rbody;
        try {
            URL url = new URL(sUrl);
            conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod(requestBody.getMethod());
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Authorization", this.getAuthorization());
            conn.setRequestProperty("Date", this.getDate());
            conn.setRequestProperty("x-oss-object-acl", "1");
            Map<String, String> metas = requestBody.getMetas();
            Iterator iter = metas.entrySet().iterator();

            while(iter.hasNext()) {
                Entry<String, String> entry = (Entry)iter.next();
                conn.setRequestProperty((String)entry.getKey(), (String)entry.getValue());
            }

            byte[] body = requestBody.getData();
            if (body != null) {
                conn.setFixedLengthStreamingMode(body.length);
            }

            conn.connect();
            if (body != null) {
                outputStream = conn.getOutputStream();
                outputStream.write(body);
                outputStream.flush();
            }

            int status = conn.getResponseCode();
            if (status != 200 && status != 204) {
                errorStream = conn.getErrorStream();
                if (errorStream != null && errorStream.available() > 0) {
                    OssError ossError = (OssError)(new XmlHandler()).handleResponse(errorStream);
                    rbody = ResponseBody.error(status, ossError);
                    return rbody;
                }
                return ResponseBody.success(status, conn.getResponseMessage());
            }

            inputStream = conn.getInputStream();
            if (inputStream != null && inputStream.available() > 0) {
                ObjectResult result = (new ObjectHandler()).handleResponse(inputStream);
                rbody = ResponseBody.success(status, result);
                return rbody;
            }

            rbody = ResponseBody.success(status, conn.getResponseMessage());
        } catch (Exception e) {
            return ResponseBody.error(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {}
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {}
            }

            if (conn != null) {
                conn.disconnect();
            }

            if (errorStream != null) {
                try {
                    errorStream.close();
                } catch (IOException e) {}
            }

        }

        return rbody;
        
    }

    @SuppressWarnings("rawtypes")
	public ResponseBody execute(RequestBody requestBody) {
        if (requestBody == null) {
            throw new RuntimeException("RequestBody is null");
        } else {
            return this.execute(requestBody.toUrlString(), requestBody);
        }
    }
	
	public static String getObjectInUrl(String path) {
		String objectRebuild = "";
		try {
			path = path.replace('/', '|');
			objectRebuild = URLEncoder.encode(path, "UTF-8");
		} catch (UnsupportedEncodingException localUnsupportedEncodingException) {
		}
		return objectRebuild;
	}

	@SuppressWarnings("unused")
	private void writeData(InputStream inputStream, OutputStream outputStream) throws IOException {
		byte[] cache = new byte[1048576];

		int length;
		while ((length = inputStream.read(cache)) != -1) {
			outputStream.write(cache, 0, length);
		}

		outputStream.flush();
	}

	private String getDate() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date date = new Date();
		return sdf.format(date);
	}

	@SuppressWarnings("unused")
	private void release(Closeable c) {
		if (c != null) {
			try {
				c.close();
			} catch (IOException e) {}

		}
	}
}
